<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style>

img, canvas { margin: 5px; border: 1px solid black; }


</style>
</head>
<body>

<script id="vs" type="not-js">
attribute vec4 position;

void main() {
  gl_Position = position;
}
</script>
<script id="minmax-fs" type="not-js">
#extension GL_EXT_draw_buffers : require
precision mediump float;

#define CELL_SIZE $(cellSize)s

uniform sampler2D u_minTexture;
uniform sampler2D u_maxTexture;
uniform vec2 u_srcResolution;  
uniform vec2 u_dstResolution;  

void main() {
  // compute the first pixel the source cell
  vec2 srcPixel = floor(gl_FragCoord.xy) * float(CELL_SIZE);
  
  // one pixel in source
  vec2 onePixel = vec2(1) / u_srcResolution;
  
  // uv for first pixel in cell. +0.5 for center of pixel
  vec2 uv = (srcPixel + 0.5) / u_srcResolution;
    
  vec4 minColor = vec4(1);
  vec4 maxColor = vec4(0);
  for (int y = 0; y < CELL_SIZE; ++y) {
    for (int x = 0; x < CELL_SIZE; ++x) {
      vec2 off = uv + vec2(x, y) * onePixel;
      minColor = min(minColor, texture2D(u_minTexture, off));
      maxColor = max(maxColor, texture2D(u_maxTexture, off));
    }
  }

  gl_FragData[0] = minColor;
  gl_FragData[1] = maxColor;
}
</script>
<script id="contrastify-fs" type="not-fs">
precision mediump float;
uniform sampler2D u_minColor;
uniform sampler2D u_maxColor;
uniform sampler2D u_texture;
uniform vec2 u_resolution;

void main() {
  vec2 uv = gl_FragCoord.xy / u_resolution;
  uv.y = 1.0 - uv.y;
  vec4 minColor = texture2D(u_minColor, vec2(0));
  vec4 maxColor = texture2D(u_maxColor, vec2(0));
  vec4 color = texture2D(u_texture, uv);     
  vec4 range = maxColor - minColor;
  gl_FragColor = vec4(((color - minColor) / range).rgb, 1);
}
</script>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>


</body>
<script>

"use strict";

var cellSize = 16;

var canvas = document.createElement("canvas");
var m4 = twgl.m4;
var gl = canvas.getContext("webgl");

var ext = gl.getExtension("WEBGL_draw_buffers");
if (!ext) {
   alert("sample requires WEBGL_draw_buffers");
}
var fsSrc = document.querySelector("#minmax-fs").text.replace("$(cellSize)s", cellSize);
var programInfo = twgl.createProgramInfo(gl, ["vs", fsSrc]);
var contrastProgramInfo = twgl.createProgramInfo(gl, ["vs", "contrastify-fs"]);

var unitQuadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);

var srcTex = twgl.createTexture(gl, { 
  src: "http://i.imgur.com/rItAVSG.jpg",
  crossOrigin: "",
  min: gl.NEAREST, 
  mag: gl.NEAREST,
  wrap: gl.CLAMP_TO_EDGE,
}, function(err, srcTex, img) {
  img.style.width = "300px";
  img.style.height = "150px";
  log("before");
  document.body.appendChild(img);
  log("after");
  document.body.appendChild(canvas);
  var framebuffers = [];
  var w = img.width;
  var h = img.height;
  while (w > 1 || h > 1) {
    w = Math.max(1, (w + cellSize - 1) / cellSize | 0);
    h = Math.max(1, (h + cellSize - 1) / cellSize | 0);
    // creates a framebuffer and creates and attaches 2 RGBA/UNSIGNED textures
    var fbi = twgl.createFramebufferInfo(gl, [
      { min: gl.NEAREST, mag: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE, },
      { min: gl.NEAREST, mag: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE, },
    ], w, h);
      ext.drawBuffersWEBGL([ext.COLOR_ATTACHMENT0_WEBGL, ext.COLOR_ATTACHMENT1_WEBGL]);
    framebuffers.push(fbi);
  }

  // need separate FBs to read the output  
  var lastFBI = framebuffers[framebuffers.length - 1];
  var minFBI = twgl.createFramebufferInfo(gl, [
    { attachment: lastFBI.attachments[0] }
  ], 1, 1);
  var maxFBI = twgl.createFramebufferInfo(gl, [
    { attachment: lastFBI.attachments[1] }
  ], 1, 1);

  var uniforms = {
    u_srcResolution: [img.width, img.height],
    u_minTexture: srcTex,
    u_maxTexture: srcTex,
  };

  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, unitQuadBufferInfo);

  var w = img.width;
  var h = img.height;
  framebuffers.forEach(function(fbi, ndx) {
    w = Math.max(1, (w + cellSize - 1) / cellSize | 0);
    h = Math.max(1, (h + cellSize - 1) / cellSize | 0);
    uniforms.u_dstResolution = [w, h];
    twgl.bindFramebufferInfo(gl, fbi);
    twgl.setUniforms(programInfo, uniforms);
    twgl.drawBufferInfo(gl, unitQuadBufferInfo);

    uniforms.u_minTexture = fbi.attachments[0];
    uniforms.u_maxTexture = fbi.attachments[1];
    uniforms.u_srcResolution = [w, h];
  });

  twgl.bindFramebufferInfo(gl, null);
  gl.useProgram(contrastProgramInfo.program);
  twgl.setUniforms(contrastProgramInfo, {
    u_resolution: [img.width, img.height],
    u_texture: srcTex,
    u_minColor: fbi.attachments[0],
    u_maxColor: fbi.attachments[1],
  });
  twgl.drawBufferInfo(gl, unitQuadBufferInfo);
});

function log() {
  var elem = document.createElement("pre");
  elem.appendChild(document.createTextNode(Array.prototype.join.call(arguments, " ")));
  document.body.appendChild(elem);
}


</script>
